package com.kelvem.saas.workbanch.fsm.core;

import com.kelvem.saas.workbanch.fsm.entity.Entity;
import com.kelvem.saas.workbanch.fsm.resolver.Many2OneResolver;
import com.kelvem.saas.workbanch.fsm.resolver.One2OneResolver;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.config.BeanDefinitionHolder;
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
import org.springframework.context.annotation.ClassPathBeanDefinitionScanner;
import org.springframework.context.annotation.ScannedGenericBeanDefinition;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;

import java.io.IOException;
import java.util.Arrays;
import java.util.Set;

/**
 * 目前仅做了Spring加载扫描, 类似拦截器, 未实际注册类, 不需要如下代码
 * for (BeanDefinition bd: candidates) {
 *     registry.registerBeanDefinition(bd.getBeanClassName(), bd);
 * }
 * @author kelvem
 */
@Slf4j
public class FsmClassPathScanner extends ClassPathBeanDefinitionScanner {
    
    public FsmClassPathScanner(BeanDefinitionRegistry registry) {
        super(registry, false);
        this.registerFilters();
    }
    
    /**
     * Configures parent scanner to search for the right interfaces. It can search
     * for all interfaces or just for those that extends a markerInterface or/and
     * those annotated with the annotationClass
     */
    public void registerFilters() {
        
        // default include filter that accepts all classes
        addIncludeFilter(new TypeFilter() {
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                // 检查类是否继承自 Entity
                try {
                    return isSubclassOfEntity(metadataReader);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }
            }

            private boolean isSubclassOfEntity(MetadataReader metadataReader) throws IOException {
                // 获取类的全限定名称
                String className = metadataReader.getClassMetadata().getClassName();

                // 检查类是否继承自 Entity
                try {
                    Class<?> clazz = Class.forName(className);
                    return Entity.class.isAssignableFrom(clazz);
                } catch (ClassNotFoundException e) {
                    throw new RuntimeException("Failed to load class " + className, e);
                }
            }
        });
        
        // exclude package-info.java
        addExcludeFilter(new TypeFilter() {
            @Override
            public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
                String className = metadataReader.getClassMetadata().getClassName();
                return className.endsWith("package-info");
            }
        });
    }
    
    /**
     * Calls the parent search that will search and register all the candidates.
     * Then the registered objects are post processed to set them as
     * MapperFactoryBeans
     */
    @Override
    public Set<BeanDefinitionHolder> doScan(String... basePackages) {
        Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
        
        if (beanDefinitions.isEmpty()) {
            logger.warn("No Entity was found in '" + Arrays.toString(basePackages) + "' package. Please check your configuration.");
        } else {
            processBeanDefinitions(beanDefinitions);
        }
        
        return beanDefinitions;
    }

    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
        for (BeanDefinitionHolder holder : beanDefinitions) {
            ScannedGenericBeanDefinition candidate = (ScannedGenericBeanDefinition) holder.getBeanDefinition();

            One2OneResolver.resolve(candidate);
            Many2OneResolver.resolve(candidate);
        }
    }

}
